XDL PlanetView 활용 첫 번째

NXPlanetView를 활용하여 구 기반 2D 도시 환경에서 기능활용법을 익히고 마우스 위치에 따른 좌표 도시, 나침반 도시, 도시 기능을 구현해 봅니다.
본 예제 프로그램은 NXPlanetView와 NXPlantLayer를 이용하여 Planet2D 모드 상에서 기본 사용법을 익히고 간단한 도시기능을 추가합니다.

들어가기 전에

설치 프로그램으로 배포되는 XDL 엔진은 Visual Studio 2022 x64 Release 버전으로, Visual Studio 2022 이상의 버전에서 사용 가능하다.
아래의 설명은 Visual Studio 2022를 기준으로 하겠다.

1 기본 프로그램 작성
1.1 Visual Studio 2022를 실행한다.
1.2 메뉴 [파일]-[새로 만들기]-[프로젝트]를 선택한다.
1.3 [새 프로젝트 만들기] 대화상자 중간의 목록에서 “Windows Forms 앱(.NET Framework)”을 선택한다.

“다음” 버튼을 눌러 프로젝트의 이름(XDL_PlanetView1)을 입력하고 [새 프로젝트] 대화상자의 “만들기” 버튼을 클릭한다. “만들기” 버튼을 누르면 프로젝트가 기본적으로 생성되고, 화면에 Form을 디자인할 수 있는 화면이 생성된다. 만약 아래와 같이 Form 디자인 화면이 생성되지 않으면, [솔루션 탐색기] 창에서 “Form1.cs”를 마우스 더블 클릭하여 디자인화면을 연다.

1.4 [도구상자]의 [항목선택]을 선택한다.

[도구 상자] 창의 “일반”에서 마우스 오른쪽 버튼을 클릭하여 생성되는 팝업메뉴에서 “항목 선택” 메뉴를 선택한다.

메뉴를 선택하면 아래와 같이 [도구 상자 항목 선택] 대화상자가 나타나며, 대화상자 아래쪽에 있는 “찾아보기” 버튼을 클릭한다. 열기 대화상자에서 “C:\Pixoneer\XDL3.0\bin\NXPlanet.dll” 파일을 선택한 후 “열기”를 클릭하고, [도구 상자 항목 선택] 대화상자의 “확인” 버튼을 클릭한다.

.NET Framework 구성요소 탭에 다양한 레이어와 NXPlanetView가 설정된다. “확인”버튼을 클릭한다. [도구상자]에 다양한 PlanetLayer들과 NXPlanetView가 추가됨을 확인한다.

1.5 솔루션 탐색기의 프로젝트 하부의 [참조]의 오른쪽 마우스 클릭을 통해 [참조추가]를 선택한다.
1.6 [찾아보기]탭으로 이동한 후 “C:\Pixoneer\XDL3.0\bin\”경로로 이동한다. Ctrl키를 누른 채 “NXDL.dll”, “NXPlanet.dll”, “NXDLgr.dll”을 선택한 후 “확인”버튼을 선택한다.
1.7 솔루션 탐색기의 프로젝트 하부의 [참조]항에 NXDL, NXDLgr, NXPlanet이 추가되었음을 확인한다.
2 프로그램 디자인
번호 Name Control type Caption Items
(1) checkBoxInverseMouseButton CheckBox InverseMouseButton
(2) checkBoxInverseMouseWheel CheckBox InverseMouseWheel
(3) checkBoxRotatable CheckBox Rotatable
(4) comboBoxGrid ComboBox None
Degrees
GARS
(5) comboBoxScale ComboBox 1000000
500000
100000
50000
10000
(6) buttonScaleApply Button Apply
(7) checkBoxShowPBP CheckBox Show PBP
(8) checkBoxShowStar CheckBox Show Star
(9) checkBoxStatusInfo CheckBox Show StatusInfo
(10) nxPlanetView1 NXPlanetView
(11) nxPlanetLayer1 NXPlanetLayer
2.1 [도구 상자]의 “SplitContainer”를 선택하고 Form1에 끌어다 놓는다.

Form1에 splitContainer1이 추가되며 아래와 같이 “Panel1”과 “Panel2”가 생긴다. Panel1에는 (1)~(9)의 컨트롤을 추가하여 디자인하고, Panel2에는 (10), (11) 컨트롤을 추가한다. Panel1의 디자인은 위의 표를 참고로 구성하도록 하며 여기에서는 생략하도록 한다.

2.2 [도구 상자]의 [일반]에서 NXPlanetView를 선택한 뒤 드래그하여 Form1의 Panel2에 놓는다. Form1 위에 NXPlanetView 컨트롤이 생성된다.
2.3 생성된 NXPlanetView 컨트롤의 오른쪽 상단에 “►” 기호를 클릭하면 “부모 컨테이너에서 도킹” 버튼이 생성되는데 이 버튼을 클릭한다.
2.4 NXPlanetView 컨트롤이 Form1에 도킹되어 전체컨트롤 영역으로 확대된다.

Form1.Designer.cs에서 보면 컨트롤 nxPlanetView1 멤버변수가 추가됨을 확인할 수 있다. NXPlanetView는 다양한 NXPlanetLayer들을 담을 수 있는 컨테이너이다. 즉, NXPlanetView는 다양한 계층의 레이어로 구성하여 구 기반 기능을 다양하게 구현할 수 있다.

2.5 [도구 상자]의 [일반]에서 NXPlanetLayer를 선택한 뒤 드래그하여 nxPlanetView1 위에 놓는다.

NXPlanetLayer는 PlanetView에 추가되어 XDL 엔진에서 발생하는 이벤트를 외부 어플리케이션에서 처리할 수 있다. Form1.Designer.cs에서 보면 다음과 같이 컨트롤 nxPlanetLayer1 멤버변수가 추가됨을 확인할 수 있다.

C#

                                
private System.Windows.Forms.SplitContainer splitContainer1;
private Pixoneer.NXDL.NXPlanet.NXPlanetView nxPlanetView1;
private Pixoneer.NXDL.NXPlanet.NXPlanetLayer nxPlanetLayer1;
                                
                        
2.6 솔루션을 빌드하고 실행한다.
3 기능 및 이벤트 추가
3.1 Form1을 선택하고 [속성]창 - [이벤트]메뉴를 선택한 뒤 “Load” 이벤트를 더블 클릭한다.

“Load” 이벤트를 더블 클릭하면, “Form1_Load” 함수가 자동적으로 추가되고 아래와 같이 추가적인 기능을 구현할 수 있도록 Form1.cs의 코드로 이동하는 것을 확인할 수 있다.

Form1_Load 함수와 함께 아래와 같이 코드를 추가한다.

C#

                                
using Pixoneer.NXDL;
using Pixoneer.NXDL.NGR;
using Pixoneer.NXDL.NXPlanet;

namespace XDL_PlanetView1	
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            nxPlanetView1.BackColor = Color.Black;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Camera 위치 설정
            XGeoPoint gpEye = XGeoPoint.FromDegree(127.4, 38.0, 1500000);
            nxPlanetView1.SetCameraPosition(gpEye, XAngle.FromDegree(0.0));

            // 컨트롤의 초기 설정
            checkBoxInverseMouseButton.Checked = nxPlanetView1.InverseMouseButton;
            checkBoxInverseMouseWheel.Checked = nxPlanetView1.InverseMouseWheel;
            checkBoxRotatable.Checked = nxPlanetView1.Rotatable;

            checkBoxShowPBP.Checked = nxPlanetView1.ShowPBP;
            checkBoxShowStar.Checked = true;
            checkBoxShowStatusInfo.Checked = nxPlanetView1.ShowStatusInfo;

            if (nxPlanetView1.GridType == NXPlanetView.eGridType.GridNone)
                comboBoxGrid.SelectedIndex = 0;
            else if (nxPlanetView1.GridType == NXPlanetView.eGridType.GridDegrees)
                comboBoxGrid.SelectedIndex = 1;
            else if (nxPlanetView1.GridType == NXPlanetView.eGridType.GridGARS)
                comboBoxGrid.SelectedIndex = 2;
            else
                comboBoxGrid.SelectedIndex = 0;

            // 화면 갱신 요청
            nxPlanetView1.RefreshScreen();
        }
    }
}
                                
                        
3.2 (1)~(9) 컨트롤을 각각 디자인모드에서 선택한 후 더블 클릭한다.

컨트롤에서 마우스로 더블 클릭하면 대표적인 이벤트에 대해 자동적으로 함수가 추가된다.

아래와 같이 코드를 추가한다.

C#

                                
private void checkBoxInverseMouseButton_CheckedChanged(object sender, EventArgs e)
{
    // PlanetView는 기본적으로 마우스 왼쪽 버튼은 화면이동을, 오른쪽 버튼은 화면회전 기능을 담당한다.
    // 이 기능을 전환하려면 NXPlanetView의 InserverMouseButton을 true로 설정하면 된다.
    nxPlanetView1.InverseMouseButton = checkBoxInverseMouseButton.Checked;
}

private void checkBoxInverseMouseWheel_CheckedChanged(object sender, EventArgs e)
{
    // PlanetView는 기본적으로 마우스 휠을 당기면 화면확대가, 밀면 화면축소가 된다.
    // 이을 전환하려면 NXPlanetView의 InverseMouseWheel을 true로 설정하면 된다.
    nxPlanetView1.InverseMouseWheel = checkBoxInverseMouseWheel.Checked;
}

private void checkBoxRotatable_CheckedChanged(object sender, EventArgs e)
{
    // NXPlanetView의 Rotatble 속성을 설정하면 화면회전 여부를 설정할 수 있다.
    nxPlanetView1.Rotatable = checkBoxRotatable.Checked;
}

private void comboBoxGrid_SelectedIndexChanged(object sender, EventArgs e)
{
    int gridIndex = comboBoxGrid.SelectedIndex;
    if (gridIndex == 0) // none
        nxPlanetView1.GridType = NXPlanetView.eGridType.GridNone;
    else if (gridIndex == 1)    // Degrees
        nxPlanetView1.GridType = NXPlanetView.eGridType.GridDegrees;
    else if (gridIndex == 2)    // GARS
        nxPlanetView1.GridType = NXPlanetView.eGridType.GridGARS;
}

private void buttonScaleApply_Click(object sender, EventArgs e)
{
    double mapAltitude = 1500000.0;
    int scaleIndex = comboBoxScale.SelectedIndex;
    if (scaleIndex == 0)   // 1000000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_1000000);
    else if (scaleIndex == 1)   // 500000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_500000);
    else if (scaleIndex == 2)   // 100000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_100000);
    else if (scaleIndex == 3)   // 50000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_50000);
    else if (scaleIndex == 4)   // 10000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_10000);


    // 현재의 camera 정보 가져오기
    NXCameraState state = nxPlanetView1.GetCameraState();

    XGeoPoint eyePos = new XGeoPoint();
    eyePos.lond = state.lonEye.deg;
    eyePos.latd = state.latEye.deg;
    eyePos.hgt = mapAltitude;

    // 위치를 유지한 상태로 카메라의 높이만 수정하여 설정
    nxPlanetView1.SetCameraPosition(eyePos, XAngle.FromDegree(0.0));
    nxPlanetView1.RefreshScreen();
}

private void checkBoxShowPBP_CheckedChanged(object sender, EventArgs e)
{
    // 지형 지도 위에 텍스트형태의 지명을 중첩하여 도시할 수 있는데, 이에 대한 여부를 설정한다.
    nxPlanetView1.ShowPBP = checkBoxShowPBP.Checked;
    nxPlanetView1.RefreshScreen();
}

private void checkBoxShowStar_CheckedChanged(object sender, EventArgs e)
{
    // 화면 축소를 해서 지도 영역 밖으로 벗어나는 경우, 배경 별을 추가도시 여부를 설정한다.
    nxPlanetView1.ShowStar = checkBoxShowStar.Checked;
    nxPlanetView1.RefreshScreen();
}

private void checkBoxShowStatusInfo_CheckedChanged(object sender, EventArgs e)
{
    // XDL 엔진의 도시 상태 정보를 화면에 도시 여부를 설정한다.
    nxPlanetView1.ShowStatusInfo = checkBoxShowStatusInfo.Checked;
    nxPlanetView1.RefreshScreen();
}
                                
                        

마지막으로 View상에서 마우스 위치에 대한 좌표를 화면도시하고, 나침반을 화면에 추가하도록 하겠다. 먼저 마우스 화면 좌표를 저장하는 변수와 화면도시를 위한 변수를 생성한다.

C#

                                
public partial class Form1 : Form
{
    private XVertex2d scrPos = new XVertex2d();
    private XTextPrinter textPrinter = new XTextPrinter();
    private XTexture compassTexture = new XTexture();

    public Form1()
    {
        InitializeComponent();
        nxPlanetView1.BackColor = Color.Black;

        Font coordFont = new Font("Gulim", 12, FontStyle.Regular | FontStyle.Bold);
        if (!textPrinter.Initialize(coordFont))
        {
            System.Diagnostics.Debug.WriteLine("Fail to initialize text printer for coordinate display!");
        }

        if (!compassTexture.Load("c:\\Pixoneer\\XDL3.0\\Resource\\compass.png "))
        {
            System.Diagnostics.Debug.WriteLine("Fail to load compass texture!");
        }
    }
    ...
}
                                
                        

나침반 이미지로 사용하는 “compass.png” 파일이 예제 코드와 같은 경로에 없으면, 예제 샘플 프로그램 압축 파일에 있는 파일을 이용하기 바란다.

3.3 nxPlanetLayer1을 선택하여 “OnWndProc” 이벤트와 “OnOrthoRender” 이벤트 함수를 추가하여 코드를 작성한다.

OnWndProc 이벤트는 NXPlanetLayer가 추가되어 있는 View의 윈도우 이벤트를 외부에서 받아서 사용할 수 있도록 한다. OnOrthoRender는 NXPlanetLayer가 추가되어 있는 View의 Ortho-rendering이 완료된 후 어플리케이션에서 추가적인 작업을 구현해야 할 필요가 있을 때 사용하면 된다. 참고로 NXPlanetView의 내부 Rendering 수행->OnRender 이벤트 발생->NXPlanetView의 내부 OrthoRendering 수행->OnOrthoRender 이벤트 발생 순서로 이루어진다. 코드는 아래와 같다.

C#

                                
private bool nxPlanetLayer1_OnWndProc(object sender, NXPlanetDrawArgs e, ref Message m)
{
    if (m.Msg == Pixoneer.NXDL.XWndMsg.XWM_MOUSEMOVE)
    {
        scrPos.x = Pixoneer.NXDL.XWndMsg.GetLowValue(m.LParam);
        scrPos.y = Pixoneer.NXDL.XWndMsg.GetHighValue(m.LParam);
        nxPlanetView1.RefreshScreen();
    }

    return default(bool);
}

private bool nxPlanetLayer1_OnOrthoRender(object sender, NXPlanetDrawArgs e)
{
    if (nxPlanetView1 == null) return false;

    XVertex3d posWorld = new XVertex3d();
    // 화면 좌표를 위경도 좌표로 변환
    XGeoPoint gpPoint = nxPlanetView1.ScreenToGeographic(scrPos.x, scrPos.y);
    posWorld.x = scrPos.x;
    posWorld.y = scrPos.y;
    posWorld.z = 0.0;

    String str = gpPoint.lond.ToString() + ", " + gpPoint.latd.ToString();

    // 화면에 텍스트 좌표를 도시
    bool result = textPrinter.Print(str, posWorld, Pixoneer.NXDL.NGR.eTextAlign.Align_Center, Color.White, true, Color.DarkBlue);

    // 나침반 도시
    if (!compassTexture.SendTextureToDevice()) return false;

    NXCameraState state = nxPlanetView1.GetCameraState();

    int nXSize = compassTexture.Width;
    int nYSize = compassTexture.Height;

    e.Graphics.glDisable(XGraphics.GL_DEPTH_TEST);
    e.Graphics.glEnable(XGraphics.GL_BLEND);

    e.Graphics.glBindTexture(XGraphics.GL_TEXTURE_2D, (uint)compassTexture.GLTextureID);
    e.Graphics.glColor3f(1.0f, 1.0f, 1.0f);

    e.Graphics.glPushMatrix();
    e.Graphics.glTranslated(100.0, 100.0);
    e.Graphics.glRotated(-state.azimuth.deg, 0.0, 0.0, 1.0);

    e.Graphics.glBegin(XGraphics.GL_QUADS);
    e.Graphics.glTexCoord2f(0, 1); e.Graphics.glVertex3d(-nXSize / 2, -nYSize / 2, 0);
    e.Graphics.glTexCoord2f(0, 0); e.Graphics.glVertex3d(-nXSize / 2, nYSize / 2, 0);
    e.Graphics.glTexCoord2f(1, 0); e.Graphics.glVertex3d(nXSize / 2, nYSize / 2, 0);
    e.Graphics.glTexCoord2f(1, 1); e.Graphics.glVertex3d(nXSize / 2, -nYSize / 2, 0);
    e.Graphics.glEnd();

    e.Graphics.glPopMatrix();

    e.Graphics.glDisable(XGraphics.GL_BLEND);
    e.Graphics.glEnable(XGraphics.GL_DEPTH_TEST);

    return default(bool);
}
                                
                        
3.4 솔루션을 빌드하고 실행한다.
1 기본 프로그램 작성
1.1 Visual Studio 2022를 실행한다.
1.2 메뉴 [파일]-[새로 만들기]-[프로젝트]를 선택한다.
1.3 [새 프로젝트 만들기] 대화상자 중간의 목록에서 “WPF 앱(.NET Framework)”을 선택한다.

“다음” 버튼을 눌러 사용자가 원하는 경로를 선택한 다음 프로젝트 이름을 “XDL_PlanetView1” 으로 입력하고 [새 프로젝트] 대화상자의 “만들기” 버튼을 클릭한다. “만들기” 버튼을 누르면 프로젝트가 기본적으로 생성되고, 화면에 Window을 디자인할 수 있는 화면이 뜬다. 만약 아래와 같은 Window 창이 생성되지 않으면, [솔루션 탐색기] 창에서 “MainWindow.xaml”를 마우스 더블클릭을 하여 창을 연다.

1.4 솔루션 탐색기의 프로젝트 하부의 [참조]의 오른쪽 마우스 클릭을 통해 [참조추가]를 선택한다.
1.5 [찾아보기]탭으로 이동한 후 “C:\Pixoneer\XDL3.0\bin\”경로로 이동한다. Ctrl키를 누른 채 “NXDL.dll”, “NXPlanet.dll”, “NXDLgr.dll” 을 선택한 후 “확인”버튼을 선택한다.
1.6 솔루션 탐색기의 프로젝트 하부의 [참조]항에 NXDL, NXDLgr, NXPlanet이 추가되었음을 확인한다.
1.7 NXPlanet 컨트롤을 활용하기 위해 MainWindow.xaml 창에 namespace를 다음과 같이 추가한다.
2 프로그램 디자인
번호 Name Control type Content Items
(1) checkBoxInverseMouseButton CheckBox InverseMouseButton
(2) checkBoxInverseMouseWheel CheckBox InverseMouseWheel
(3) checkBoxRotatable CheckBox Rotatable
(4) comboBoxGrid ComboBox None
Degrees
GARS
(5) comboBoxScale ComboBox 1000000
500000
100000
50000
10000
(6) buttonScaleApply Button Apply
(7) checkBoxShowPBP CheckBox Show PBP
(8) checkBoxShowStar CheckBox Show Star
(9) checkBoxStatusInfo CheckBox Show StatusInfo
(10) nxPlanetView1 NXPlanetView
(11) nxPlanetLayer1 NXPlanetLayer
2.1 MainWindow.xaml 창에서 기본으로 생성된 Grid 레이아웃에 Grid.ColumnDefinition을 이용하여 세 개의 Column을 생성한다. 첫 번째 Column에는 위의 표를 참고하여 디자인하는 작업을 한다. (①~⑨). 두 번째 Column은 GridSpiltter 컨트롤을 정의하고 세 번째 Column은 NXPlanetView 컨트롤과 NXPlanetLayer을 배치한다.

먼저 Grid를 세 개의 Column으로 나눈다. 그 다음으로 첫 번째 Column에 도구상자를 이용하여 CheckBox와 Button 컨트롤 등을 이용해 디자인한다(표 참고). 자세한 XAML코드는 샘플을 참고한다.

XAML

                                <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="210"/>
        <ColumnDefinition Width="5"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid Grid.Column="0">
    …
    </Grid>
</Grid>
                        
2.2 두 번째 Column에 GridSplitter을 배치하여 좌우를 구분한다. 아래의 코드를 추가하거나 도구상자에서 GridSplitter을 끌어다 배치한다.

XAML

                                <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="210"/>
        <ColumnDefinition Width="5"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid Grid.Column="0">
    …
    </Grid>
    <GridSplitter Grid.Column="1"/>
</Grid>
                        
2.3 세 번째 Cell에 WindowsFormsHost 컨트롤을 배치한다. 이는 Window forms를 Hosting 해주는 개체이며 Window Forms Control을 사용할 때 이용된다. (WindowFormIntegration와 System.Window.Forms 을 어셈블리에 대한 참조 추가를 한다.)
2.4 앞서 추가한 namespace인 nxPlanet을 이용하여 NXPlanetView컨트롤을 불러온다. 이 NXPlanetView의 이름(Name)은 nxPlanetView1으로 한다.

XAML

                                <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="210"/>
        <ColumnDefinition Width="5"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid Grid.Column="0">
    …
    </Grid>
    <GridSplitter Grid.Column="1"/>
    <WindowsFormsHost Grid.Column="2" Margin="3,0,0,3">
        <nxPlanet:NXPlanetView x:Name="nxPlanetView1">
            
        </nxPlanet:NXPlanetView>
    </WindowsFormsHost>
</Grid>
                        
2.5 이어서 NXPlanetView 컨트롤 위에 NXPlanetLayer을 올려 놓는다. NXPlanetLayer의 이름(Name)은 nxPlanetLayer1이라고 한다.

XAML

                                <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="210"/>
        <ColumnDefinition Width="5"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid Grid.Column="0">
    …
    </Grid>
    <GridSplitter Grid.Column="1"/>
    <WindowsFormsHost Grid.Column="2" Margin="3,0,0,3">
        <nxPlanet:NXPlanetView x:Name="nxPlanetView1">
           <nxPlanet:NXPlanetView.Controls>
                    <nxPlanet:NXPlanetLayer x:Name="nxPlanetLayer1"/>
           </nxPlanet:NXPlanetView.Controls>
        </nxPlanet:NXPlanetView>
    </WindowsFormsHost>
</Grid>
                        

NXPlanetView는 다양한 NXPlanetLayer들을 담을 수 있는 컨테이너이다 즉, NXPlanetView는 다양한 레이어를 구성하여 구 기반 기능을 다양하게 구현할 수 있다. NXPlanetLayer는 PlanetView에 추가되어 XDL 엔진에서 발생하는 이벤트를 외부 어플리케이션에서 처리할 수 있다. 디자인이 완료되면 아래와 같은 창을 얻을 수 있다.

2.6 솔루션을 빌드하고 실행한다
3 기능 및 이벤트 추가
3.1 Window 창을 선택하고 [속성]창 - [이벤트]메뉴를 선택한 뒤 “Loaded” 이벤트를 더블 클릭한다.

“Loaded” 이벤트를 더블 클릭하면, “Window_Loaded” 함수가 자동적으로 추가되고 아래와 같이 추가적인 기능을 구현할 수 있도록 MainWindow.xaml.cs으로 이동하는 것을 확인할 수 있다. 아래와 같이 코드를 추가한다.

C#

                                using Pixoneer.NXDL;
using Pixoneer.NXDL.NGR;
using Pixoneer.NXDL.NXPlanet;

namespace XDL_PlanetView1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            nxPlanetView1.BackColor = System.Drawing.Color.Black;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Camera 위치 설정
            XGeoPoint gpEye = XGeoPoint.FromDegree(127.4, 38.0, 1500000);
            nxPlanetView1.SetCameraPosition(gpEye, XAngle.FromDegree(0.0));

            // 컨트롤의 초기 설정
            checkBoxInverseMouseButton.IsChecked = nxPlanetView1.InverseMouseButton;
            checkBoxInverseMouseWheel.IsChecked = nxPlanetView1.InverseMouseWheel;
            checkBoxRotatable.IsChecked = nxPlanetView1.Rotatable;

            checkBoxShowPBP.IsChecked = nxPlanetView1.ShowPBP;
            checkBoxShowStar.IsChecked = true;
            checkBoxStatusInfo.IsChecked = nxPlanetView1.ShowStatusInfo;

            if (nxPlanetView1.GridType == NXPlanetView.eGridType.GridNone)
                comboBoxGrid.SelectedIndex = 0;
            else if (nxPlanetView1.GridType == NXPlanetView.eGridType.GridDegrees)
                comboBoxGrid.SelectedIndex = 1;
            else if (nxPlanetView1.GridType == NXPlanetView.eGridType.GridGARS)
                comboBoxGrid.SelectedIndex = 2;
            else
                comboBoxGrid.SelectedIndex = 0;

            // 화면 갱신 요청
            nxPlanetView1.RefreshScreen();
        }
    }
}

                        
3.2 (1)~(9) 컨트롤을 각각 디자인모드에서 선택한 후 더블 클릭한다.

컨트롤에서 마우스로 더블 클릭하면 대표적인 이벤트에 대해 자동적으로 함수가 추가된다. 아래와 같이 코드를 MainWindow.xaml.cs에 추가한다.

C#

                                private void checkBoxInverseMouseButton_Checked(object sender, RoutedEventArgs e)
{
    // PlanetView는 기본적으로 마우스 왼쪽 버튼은 화면 이동을, 오른쪽 버튼은 화면회전 기능을 담당한다.
    // 이 기능을 전환하려면 NXPlanetView의 InverseMouseButton을 true로 설정하면 된다.
    nxPlanetView1.InverseMouseButton = true;
}
        
private void checkBoxInverseMouseButton_UnChecked(object sender, RoutedEventArgs e)
{
    // PlanetView는 기본적으로 마우스 왼쪽 버튼은 화면 이동을, 오른쪽 버튼은 화면회전 기능을 담당한다.
    // 이 기능을 전환 후 해제하려면 NXPlanetView의 InverseMouseButton을 false로 설정하면 된다.                     
    nxPlanetView1.InverseMouseButton = false;
}

private void checkBoxInverseMouseWheel_Checked(object sender, RoutedEventArgs e)
{
    // PlanetView는 기본적으로 마우스 왼쪽 버튼은 화면 이동을, 오른쪽 버튼은 화면회전 기능을 담당한다.
    // 이 기능을 전환하려면 NXPlanetView의 InverseMouseButton을 true로 설정하면 된다.
    nxPlanetView1.InverseMouseWheel = true;
}

private void checkBoxInverseMouseWheel_Unchecked(object sender, RoutedEventArgs e)
{
    // PlanetView는 기본적으로 마우스 왼쪽 버튼은 화면 이동을, 오른쪽 버튼은 화면회전 기능을 담당한다.
    // 이 기능을 전환 후 해제하려면 NXPlanetView의 InverseMouseButton을 false로 설정하면 된다.
    nxPlanetView1.InverseMouseWheel = false;
}

private void checkBoxRotatable_Checked(object sender, RoutedEventArgs e)
{
    // NXPlanetView의 Rotatable 속성을 설정하려면 화면회전 여부를 설정할 수 있다.
    nxPlanetView1.Rotatable = true;
}

private void checkBoxRotatable_Unchecked(object sender, RoutedEventArgs e)
{
    // NXPlanetView의 Rotatable 속성을 설정하려면 화면회전 여부를 설정할 수 있다.
    nxPlanetView1.Rotatable = false;
}

private void comboBoxGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (comboBoxGrid.SelectedIndex == 0)  // None
    {
        nxPlanetView1.GridType = NXPlanetView.eGridType.GridNone;
    }
    else if (comboBoxGrid.SelectedIndex == 1)  // Degree
    {
        nxPlanetView1.GridType = NXPlanetView.eGridType.GridDegrees;
    }
    else if (comboBoxGrid.SelectedIndex == 2)  // GARS
    {
        nxPlanetView1.GridType = NXPlanetView.eGridType.GridGARS;
    }
}

                        

이어서 코드를 추가한다.

C#

                                private void buttonScaleApply_Click(object sender, RoutedEventArgs e)
{
    double mapAltitude = 1500000.0;
    int scaleIndex = comboBoxScale.SelectedIndex;
    if (scaleIndex == 0)        // 1000000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_1000000);
    else if (scaleIndex == 1)   // 500000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_500000);
    else if (scaleIndex == 2)   // 100000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_100000);
    else if (scaleIndex == 3)   // 50000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_50000);
    else if (scaleIndex == 4)   // 10000
        mapAltitude = nxPlanetView1.GetMapAltitude(NXPlanetView.eMapScale.Scale_10000);

    // 현재의 Camera 정보 가져오기
    NXCameraState state = nxPlanetView1.GetCameraState();

    XGeoPoint eyePos = new XGeoPoint();
    eyePos.lond = state.lonEye.deg;
    eyePos.latd = state.latEye.deg;
    eyePos.hgt = mapAltitude;

    // 위치를 유지한 상태로 카메라의 높이만 수정하여 설정
    nxPlanetView1.SetCameraPosition(eyePos, XAngle.FromDegree(0.0));
    nxPlanetView1.RefreshScreen();
}

private void checkBoxShowPBP_Checked(object sender, RoutedEventArgs e)
{
    // 지형 지도 위에 텍스트형태의 지명을 중첩하여 도시할 수 있는데, 이에 대한 여부를 설정한다.
    nxPlanetView1.ShowPBP = true;
    nxPlanetView1.RefreshScreen();
}
private void checkBoxShowPBP_Unchecked(object sender, RoutedEventArgs e)
{
    // 지형 지도 위에 텍스트형태의 지명을 중첩하여 도시할 수 있는데, 이에 대한 여부를 설정한다.
    nxPlanetView1.ShowPBP = false;
    nxPlanetView1.RefreshScreen();
}

private void checkBoxShowStar_Checked(object sender, RoutedEventArgs e)
{
    // 화면 축소를 해서 지도 영역 밖으로 벗어나는 경우, 배경 별을 추가도시 여부를 설정한다.
    nxPlanetView1.ShowStar = true;
    nxPlanetView1.RefreshScreen();
}

private void checkBoxShowStar_Unchecked(object sender, RoutedEventArgs e)
{
    // 화면 축소를 해서 지도 영역 밖으로 벗어나는 경우, 배경 별을 추가도시 여부를 설정한다.
    nxPlanetView1.ShowStar = false;
    nxPlanetView1.RefreshScreen();
}

private void checkBoxStatusInfo_Checked(object sender, RoutedEventArgs e)
{
    // XDL 엔진의 도시 상태 정보를 화면에 도시 여부를 설정한다.
    nxPlanetView1.ShowStatusInfo = true;
    nxPlanetView1.RefreshScreen();
}
private void checkBoxStatusInfo_Unchecked(object sender, RoutedEventArgs e)
{
    // XDL 엔진의 도시 상태 정보를 화면에 도시 여부를 설정한다.
    nxPlanetView1.ShowStatusInfo = false;
    nxPlanetView1.RefreshScreen();
}

                        

마지막으로 View상에서 마우스 위치에 대한 좌표를 화면도시하고, 나침반을 화면에 추가하도록 하겠다. 먼저 마우스 화면 좌표를 저장하는 변수와 화면도시를 위한 변수를 생성한다.

C#

                                public partial class MainWindow : Window
{
// 마우스 화면 좌표를 저장하는 변수와 화면도시를 위한 변수생성
    private XVertex2d scrPos = new XVertex2d();
    private XTextPrinter textPrinter = new XTextPrinter();
    private XTexture compassTexture = new XTexture();
    public MainWindow()
    {
        InitializeComponent();
        nxPlanetView1.BackColor = System.Drawing.Color.Black;

        Font coordFont = new Font("Gulim", 12, System.Drawing.FontStyle.Regular | System.Drawing.FontStyle.Bold);
        if (!textPrinter.Initialize(coordFont))
        {
            System.Diagnostics.Debug.WriteLine("Fail to initialize text printer for coordinate display!");
        }

        if (!compassTexture.Load("c:\\Pixoneer\\XDL3.0\\Resource\\compass.png"))
        {
            System.Diagnostics.Debug.WriteLine("Fail to load compass texture!");
        }
    }
}

                        

나침반 이미지로 사용하는 “compass.png” 파일이 예제 코드와 같은 경로에 없으면, 예제 샘플 프로그램 압축 파일에 있는 파일을 이용하기 바란다.

3.3 nxPlanetLayer1을 선택하여 “OnWndProc” 이벤트와 “OnOrthoRender” 이벤트 함수를 추가하여 코드를 작성한다.

OnWndProc 이벤트는 NXPlanetLayer가 추가되어 있는 View의 윈도우 이벤트를 외부에서 받아서 사용할 수 있도록 한다. OnOrthoRender는 NXPlanetLayer가 추가되어 있는 View의 Ortho-rendering이 완료된 후 어플리케이션에서 추가적인 작업을 구현해야 할 필요가 있을 때 사용하면 된다. 참고로 NXPlanetView의 내부 Rendering 수행->OnRender 이벤트 발생->NXPlanetView의 내부 OrthoRendering 수행->OnOrthoRender 이벤트 발생 순서로 이루어진다. 코드는 아래와 같다.

C#

                                private bool nxPlanetLayer1_OnWndProc(object sender, NXPlanetDrawArgs e, ref System.Windows.Forms.Message m)
{
    if (m.Msg == Pixoneer.NXDL.XWndMsg.XWM_MOUSEMOVE)
    {
        scrPos.x = Pixoneer.NXDL.XWndMsg.GetLowValue(m.LParam);
        scrPos.y = Pixoneer.NXDL.XWndMsg.GetHighValue(m.LParam);
        nxPlanetView1.RefreshScreen();
    }
    return default(bool);
}

private bool nxPlanetLayer1_OnOrthoRender(object sender, NXPlanetDrawArgs e)
{
    if (nxPlanetView1 == null) return false;

    XVertex3d posWorld = new XVertex3d();
    // 화면 좌표를 위경도 좌표로 변환
    XGeoPoint gpPoint = nxPlanetView1.ScreenToGeographic(scrPos.x, scrPos.y);
    posWorld.x = scrPos.x;
    posWorld.y = scrPos.y;
    posWorld.z = 0.0;

    String str = gpPoint.lond.ToString() + ", " + gpPoint.latd.ToString();

    // 화면에 텍스트 좌표를 도시
    bool result = textPrinter.Print(str, posWorld, Pixoneer.NXDL.NGR.eTextAlign.Align_Center, System.Drawing.Color.White, true, System.Drawing.Color.DarkBlue);

    // 나침반 도시
    if (!compassTexture.SendTextureToDevice()) return false;

    NXCameraState state = nxPlanetView1.GetCameraState();

    int nXSize = compassTexture.Width;
    int nYSize = compassTexture.Height;

    e.Graphics.glDisable(XGraphics.GL_DEPTH_TEST);
    e.Graphics.glEnable(XGraphics.GL_BLEND);

    e.Graphics.glBindTexture(XGraphics.GL_TEXTURE_2D, (uint)compassTexture.GLTextureID);
    e.Graphics.glColor3f(1.0f, 1.0f, 1.0f);

    e.Graphics.glPushMatrix();
    e.Graphics.glTranslated(100.0, 100.0);
    e.Graphics.glRotated(-state.azimuth.deg, 0.0, 0.0, 1.0);

    e.Graphics.glBegin(XGraphics.GL_QUADS);
    e.Graphics.glTexCoord2f(0, 1); e.Graphics.glVertex3d(-nXSize / 2, -nYSize / 2, 0);
    e.Graphics.glTexCoord2f(0, 0); e.Graphics.glVertex3d(-nXSize / 2,  nYSize / 2, 0);

    e.Graphics.glTexCoord2f(1, 0); e.Graphics.glVertex3d(nXSize / 2, nYSize / 2, 0);
    e.Graphics.glTexCoord2f(1, 1); e.Graphics.glVertex3d(nXSize / 2, -nYSize / 2, 0);
    e.Graphics.glEnd();

    e.Graphics.glPopMatrix();

    e.Graphics.glDisable(XGraphics.GL_BLEND);
    e.Graphics.glEnable(XGraphics.GL_DEPTH_TEST);

    return default(bool);
}
                                
                        
3.4 솔루션을 빌드하고 실행한다.